﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Runtime.Serialization;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Text;
using DeSleeper.Library;
using DeSleeper.Service.Properties;
using NLog;

namespace DeSleeper.Service
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class ProxyRegister : IProxyRegister
    {
        private static readonly Logger _logger = LogManager.GetCurrentClassLogger();

        #region Initialization and Instance Management
		private static ServiceHost _host;
        private static ProxyRegister _instance;

        /// <exception cref="InvalidOperationException">Proxy Register host must be initialized before being used.</exception>
    	public static ProxyRegister Instance
        {
            get
            {
                if (_instance == null) throw new InvalidOperationException("Proxy Register host must be initialized before being used.");
                return _instance;
            }
        }

        public static void StartLocally()
        {
            if (_instance == null)
				_instance = new ProxyRegister();

			_host = new ServiceHost(_instance);
			_host.Credentials.Peer.MeshPassword = "Test";
			_host.Credentials.Peer.MessageSenderAuthentication.CertificateValidationMode = X509CertificateValidationMode.None;
            //serviceHost.Credentials.Peer.MessageSenderAuthentication.TrustedStoreLocation = StoreLocation.LocalMachine;
			_host.Open();
			_logger.Info("ProxyRegisterService started at {0}", 
				_host.BaseAddresses.Aggregate(string.Empty, (outString, address) => outString + address));

            var factory = new DuplexChannelFactory<IProxyRegister>(_instance, ConfigurationBuilder.PeerClientEndpointName);
            var proxy = factory.CreateChannel();

            registerMyMachineWithEndpoint(proxy);
        	_logger.Info("Machine registered with mesh");
        }

        public static void RegisterWithLiveMesh()
        {
            var data = MeshData.GetFromCloud(Settings.Default.CloudSettings);
            data.ProcessingRequest += ((sender, args) =>
                                           {
                                               HostInfo hostInfo;
                                               if (TargetCache.Instance.TryGetValue(args.Data.HostName, true, out hostInfo))
                                                   args.Cancel = false;
                                           });
            data.ProcessRequest += ((sender, args) =>
                                        {
                                            if (args.Data.MacAddress == null)
                                                TargetCache.Instance.WakeUpRegisteredHost(args.Data.HostName);
                                            else
                                                WakeOnLanHelper.WakeUp(args.Data.MacAddress);
                                        });
            data.RegisterWithLiveMesh();
        }

        //private static X509Certificate2 GetCertificate()
        //{
        //    var certStore = new X509Store(StoreLocation.LocalMachine);
        //    var certs = certStore.Certificates.Find(X509FindType.FindBySubjectName, "DeSleeper", true);

        //    X509Certificate2.
        //    if (certs.Count == 0) certStore.Add(new X509Certificate2() { SubjectName = "DeSleeper" });

        //}

		public static void Stop()
		{
            // Service never started successfully or is already stopped.
            if (_host == null)
                return;

			_host.Close();
			_host = null;
		}
        #endregion

    	#region Registration
        protected static IProxyRegister Callback { get { return OperationContext.Current.GetCallbackChannel<IProxyRegister>(); }}
        public void RegisterForSubnet(string host, IPAddress current, IPAddress subnet, string macAddress)
        {
            TargetCache.Instance.AddHostInfo(host, current, macAddress, subnet);
            registerMyMachineWithEndpoint(Callback);
        }

        private static void registerMyMachineWithEndpoint(IProxyRegister proxy)
        {
        	var endpoints = from networkInterface in NetworkInterface.GetAllNetworkInterfaces()
        	                        from address in networkInterface.GetIPProperties().UnicastAddresses
        	                        where networkInterface.OperationalStatus == OperationalStatus.Up
        	                        select new {networkInterface, address};

			foreach (var endpoint in endpoints)
			{
				proxy.RegisterForSubnet(Dns.GetHostName(), endpoint.address.Address, endpoint.address.IPv4Mask,
										endpoint.networkInterface.GetPhysicalAddress().ToString());
			}
        }
        #endregion

    	public static bool IsConfigured()
        {
            return new ConfigurationBuilder().IsRegisterServiceConfigured();
        }
    }
}
